package cn.anecansaitin.firecrafting.common.crafting;

import cn.anecansaitin.firecrafting.ModConstant;
import cn.anecansaitin.firecrafting.api.common.IResultType;
import cn.anecansaitin.firecrafting.api.common.block.entity.IFireInterfaceEntity;
import cn.anecansaitin.firecrafting.api.client.crafting.IJEIRecipeRender;
import cn.anecansaitin.firecrafting.client.crafting.FireEnergyRecipeRender;
import cn.anecansaitin.firecrafting.common.Config;
import cn.anecansaitin.firecrafting.common.util.ModJsonReader;
import cn.anecansaitin.firecrafting.common.util.Directions;
import com.google.common.collect.ImmutableList;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.NonNullList;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.GsonHelper;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.item.crafting.RecipeSerializer;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.phys.AABB;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.energy.CapabilityEnergy;
import net.minecraftforge.energy.IEnergyStorage;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.ForgeRegistryEntry;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.List;

public class FireEnergyRecipe extends SimpleFireRecipe {
    //产物能量的数量
    private final int outputs;

    @Override
    public IJEIRecipeRender getRender() {
        return FireEnergyRecipeRender.INSTANCE;
    }

    //构造函数
    public FireEnergyRecipe(ResourceLocation id, ImmutableList<Block> fireBlock, NonNullList<Ingredient> inputs, int outputs) {
        super(id, fireBlock, inputs);
        this.outputs = outputs;
    }

    //在配方正确时进行一系列行为
    @Override
    public boolean doAction(List<ItemStack> stacks, BlockPos pos, Level world) {
        //检测环境和配方
        if (match(stacks, pos, world)) {
            //用于记录剩余能量
            int remain = outputs;
            //为火焰六个面的能量进行检测，每个面都尝试存入能量
            for (Direction direction : Directions.ALL_SIDE) {
                //获取指定方向的能量实体
                BlockEntity entity = world.getBlockEntity(pos.relative(direction));
                //判断能量实体存在
                if (entity != null) {
                    //获取指定方向的能量能力（获取朝向火焰的那个面）
                    LazyOptional<IEnergyStorage> cap = entity.getCapability(CapabilityEnergy.ENERGY, direction.getOpposite());
                    //判断能力存在
                    if (cap.isPresent()) {
                        //获取能力对象
                        IEnergyStorage energy = cap.orElseThrow(RuntimeException::new);
                        //存入能量并减少剩余能量
                        remain = remain - energy.receiveEnergy(remain, false);
                    }
                }
                //判断剩余能量耗尽
                if (remain <= 0) {
                    //退出循环
                    break;
                }
            }
            //判断能量未耗尽
            if (remain > 0) {
                //获取火焰3*3的范围内实体
                List<Entity> entities = world.getEntities(null, new AABB(pos.getX() - 1, pos.getY() - 1, pos.getZ() - 1, pos.getX() + 2, pos.getY() + 2, pos.getZ() + 2));
                //遍历每个实体
                for (Entity entity : entities) {
                    //对实体施加伤害 ~~~~漏电啦！
                    if (entity instanceof LivingEntity)
                        entity.hurt(DamageSource.LIGHTNING_BOLT, remain * 0.0015f);
                }
            }
            return true;
        }
        return false;
    }

    //适用于火焰接口的行为
    @Override
    public int doAction(IFireInterfaceEntity fireInterface) {
        //获取该接口能处理的类型
        List<IResultType> types = fireInterface.actionType();
        //判断接口支持的类型
        if (types.stream().anyMatch(IResultType.ENERGY::equals)) {
            //检测配方
            if (!match(fireInterface.getMaterial(), fireInterface.getFire())) {
                return -1;
            }
            //获取能量能力
            LazyOptional<IEnergyStorage> energyHandler = fireInterface.getCapForOutput(CapabilityEnergy.ENERGY);
            //如果不能力存在
            if (!energyHandler.isPresent()) {
                return -1;
            }
            //获取能量对象
            IEnergyStorage energy = energyHandler.orElseThrow(() -> new RuntimeException("不可能触发的异常"));
            //尝试存入
            int i = energy.receiveEnergy(outputs, true);
            //检测可存入能量是否等于总输出
            if (i != outputs) {
                return 0;
            }
            //正真存入
            energy.receiveEnergy(outputs, false);
            return 1;
        }

        return -1;
    }

    public int getOutputs() {
        return outputs;
    }

    //返回配方指定的序列化器
    @Override
    public @NotNull RecipeSerializer<?> getSerializer() {
        return ModRecipeTypes.FIRE_ENERGY_SERIALIZER.get();
    }

    //序列化器实现类
    public static class Serializer extends ForgeRegistryEntry<RecipeSerializer<?>> implements RecipeSerializer<FireEnergyRecipe> {
        public static final String NAME = ModConstant.MOD_ID + ":fire_energy_crafting";

        //从json读取配方
        @Override
        public @NotNull FireEnergyRecipe fromJson(@NotNull ResourceLocation id, @NotNull JsonObject json) {
            //获取配方物品成分
            NonNullList<Ingredient> nonnulllist = ModJsonReader.ingredientsFromJson(GsonHelper.getAsJsonArray(json, "ingredients"));
            //判断配方成分的可用性
            if (nonnulllist.isEmpty()) {
                throw new JsonParseException("火焰燃烧(能量)配方内没有任何原料 No ingredients for fire block recipe");
            } else if (nonnulllist.size() > Config.WORLD_MAX_COUNT.get()) {
                throw new JsonParseException("火焰燃烧(能量)配方内的原料超出上限" + Config.WORLD_MAX_COUNT.get() + "  Too many ingredients for fire block recipe. The maximum is " + Config.WORLD_MAX_COUNT.get());
            }
            //读取能量
            int output = json.get("output").getAsInt();
            //获取可用火焰
            ImmutableList<Block> fire = ImmutableList.copyOf(ModJsonReader.blocksFromJson(json, "fire"));
            //判断火焰可用性
            if (fire.isEmpty()) {
                throw new JsonParseException("火焰燃烧(能量)配方内没有指定火焰 No fire block for fire block recipe");
            }
            return new FireEnergyRecipe(id, fire, nonnulllist, output);
        }

        //从网络读取配方
        @Nullable
        @Override
        public FireEnergyRecipe fromNetwork(@NotNull ResourceLocation id, @NotNull FriendlyByteBuf buffer) {
            //获取火焰数量
            int fireCount = buffer.readVarInt();
            //创建list构造器
            ImmutableList.Builder<Block> fireBuilder = ImmutableList.builder();
            //从buffer中读取火焰
            for (int i = 0; i < fireCount; i++) {
                fireBuilder.add(ForgeRegistries.BLOCKS.getValue(buffer.readResourceLocation()));
            }
            //获取产物能量
            int output = buffer.readVarInt();
            //从buffer中获取配方成分数量并创建集合
            NonNullList<Ingredient> input = NonNullList.withSize(buffer.readVarInt(), Ingredient.EMPTY);
            //从buffer中获取指定数量成分
            for (int i = 0; i < input.size(); i++) {
                input.set(i, Ingredient.fromNetwork(buffer));
            }
            return new FireEnergyRecipe(id, fireBuilder.build(), input, output);
        }

        //将配方传入网络
        @Override
        public void toNetwork(FriendlyByteBuf buffer, FireEnergyRecipe recipe) {
            //获取配方火焰
            ImmutableList<Block> fireBlock = recipe.fireBlock;
            //向buffer写入火焰数量
            buffer.writeVarInt(fireBlock.size());
            //向buffer写入火焰
            fireBlock.forEach((b) -> buffer.writeResourceLocation(b.getRegistryName()));
            //获取配方产物能量
            buffer.writeVarInt(recipe.outputs);
            //获取配方成分
            NonNullList<Ingredient> inputs = recipe.inputs;
            //向buffer写入成分数量
            buffer.writeVarInt(inputs.size());
            //向buffer写入成分
            inputs.forEach((i) -> i.toNetwork(buffer));
        }
    }
}
